#include "dnd.h"

static const char * class_name = "EasyGUI DND";

static void afc_easygui_widget_int_set_dnd_dest_connection ( EasyGUIContext * context, GtkWidget * w, DynamicClass * dyn );
static void afc_easygui_widget_int_set_dnd_source_connection ( EasyGUIContext * context, GtkWidget * w, DynamicClass * dyn );

static void afc_easygui_event_dnd_get ( GtkWidget * widget, GdkDragContext * dc, GtkSelectionData * selection_data, guint info, guint t, gpointer data);
static void afc_easygui_event_dnd_received ( GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, GtkSelectionData * selection_data, guint info, guint t, gpointer data);

// {{{ afc_easygui_widget_dnd_dest_connect ( dyn, w, event, func_name, sig_id, default_func ) ****
/*
@node afc_easygui_widget_dbd_dest_connect
	Connects the widget event with the user provided function callback

	INPUTS:	
		- dyn		- Dynamic Class to connect the event to
		- w		- GtkWidget to connect event to (if NULL it will be set to ewidget->gtk_handle)
		- event		- Event Name (GTK Name)
		- func_name	- User selected function name
		- sig_id	- Place where to store Signal value
		- default_func	- Pointer to the plugin-provided default function
				  NOTE: if it is NULL, then the event will be stored in the callback dictionary
					but the function will not be connected.

	SEE ALSO:	afc_easygui_widget_store_function()
@endnode
*/
int afc_easygui_widget_dnd_dest_connect ( DynamicClass * dyn, GtkWidget * w, const char * func_name )
{
	EasyGUIWidgetData * ewidget;
	EasyGUIContext * context = WIDGET_GET_CONTEXT ( dyn );
	guint signal = 0;

	afc_dprintf ( "%s: registering DND dest\n", __FUNCTION__ );
	afc_easygui_widget_int_set_dnd_dest_connection ( context, w, dyn );

	// If the func_name is not specified, we do not link the event to the widget
	if ( func_name == NULL ) {
		return ( AFC_NO_ERR );
	}

	WIDGET_GET_EWIDGET ( dyn, ewidget );
	
	/* connect the signal to the GTK widget */
	if ( w == NULL ) w = ewidget->gtk_handle;

	signal = g_signal_connect ( G_OBJECT ( w ), "drag_data_received", G_CALLBACK ( afc_easygui_event_dnd_received ), dyn );
	return ( afc_easygui_widget_store_function ( context, dyn, "drag_data_received", func_name ) );
}
// }}}
// {{{ afc_easygui_widget_dnd_source_connect ( dyn,  w,  func_name )
int afc_easygui_widget_dnd_source_connect ( DynamicClass * dyn, GtkWidget * w, const char * func_name )
{
	EasyGUIWidgetData * ewidget;
	EasyGUIContext * context = WIDGET_GET_CONTEXT ( dyn );
	guint signal = 0;

	afc_dprintf ( "%s: registering DND source\n", __FUNCTION__ );

	afc_easygui_widget_int_set_dnd_source_connection ( context, w, dyn );

	// If the func_name is not specified, we do not link the event to the widget
	if ( func_name == NULL ) {
		return ( AFC_NO_ERR );
	}

	WIDGET_GET_EWIDGET ( dyn, ewidget );
	
	/* connect the signal to the GTK widget */
	if ( w == NULL ) w = ewidget->gtk_handle;

	signal = g_signal_connect ( G_OBJECT ( w ), "drag_data_get", G_CALLBACK ( afc_easygui_event_dnd_get ), dyn );
	return ( afc_easygui_widget_store_function ( context, dyn, "drag_data_get", func_name ) );
}
// }}}

// =================================================================================================================================
// EVENTS
// =================================================================================================================================

/*
 *	DND "drag_data_get" handler, for handling requests for DND
 *	data on the specified widget. This function is called when
 *	there is need for DND data on the source, so this function is
 *	responsable for setting up the dynamic data exchange buffer
 *	(DDE as sometimes it is called) and sending it out.
 */
static void afc_easygui_event_dnd_get ( GtkWidget * widget, GdkDragContext * dc, GtkSelectionData * selection_data, guint info, guint t, gpointer data)
{
	gboolean data_sent = FALSE;
	DynamicClass * dyn = data;
	EasyGUIContext *context = WIDGET_GET_CONTEXT(dyn);
	char * str;
	EasyGUISignalFunc dnd_get;
	
	afc_dprintf ( "%s: drag get\n", __FUNCTION__ );

        if ( ( widget == NULL ) || ( dyn == NULL ) || ( dc == NULL ) || ( context == NULL ) ) {
		afc_dprintf(">>> Widget, Dyn, dc or context are null: %p %p %p %p\n", widget, dyn, dc, context);
		return;
	}
	
	//Check if dynamic class supports drag_data_get method
	if ( (dnd_get = afc_dictionary_get ( dyn->private_vars, "drag_data_get" )) == NULL ) {
		afc_dprintf(">>> NIENTE METODO drag_data_get\n");
		return;
	}

	// Attemps to execute the method binded to "dnd_get" on the Dynamic Class, which is responsible
	// of setting the dyn->result with an AFC string containing the DND message to be sent.
	if ( dnd_get(dyn, context) == AFC_ERR_NO_ERROR ) 
	{
		str = dyn->result;
		gtk_selection_data_set ( selection_data, GDK_SELECTION_TYPE_STRING, 8, str, afc_string_len ( str ) );
		data_sent = TRUE;
	}

	/* If we did not send out any data (for whatever reason),
	 * then send out an error response since this function
	 * needs to gaurantee a response when reasonably possible.
	 */
	if ( !data_sent )
	{
		const gchar *cstrptr = "Error";
		gtk_selection_data_set( selection_data, GDK_SELECTION_TYPE_STRING, 8, cstrptr, strlen ( cstrptr ) );
	}
}


static void afc_easygui_event_dnd_received ( GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, GtkSelectionData * selection_data, guint info, guint t, gpointer data)
{
	gboolean same;
	GtkWidget *source_widget;
	DynamicClass * dyn = data;
	EasyGUIContext *context = WIDGET_GET_CONTEXT(dyn);
	typedef int(*DragSetFunction)(DynamicClass *, EasyGUIContext *, void *, int, int);
	DragSetFunction dnd_set;

	afc_dprintf ( "%s: DND received\n", __FUNCTION__ );
	
	
        if ( ( widget == NULL ) || ( dyn == NULL ) || ( drag_context == NULL ) || ( context == NULL ) ) {
		afc_dprintf(">>> Widget, Dyn, dc or context are null: %p %p %p %p\n", widget, dyn, drag_context, context);
		return;
	}
	
	//Check if dynamic class supports drag_data_get method
	if ( (dnd_set = afc_dictionary_get ( dyn->private_vars, "drag_data_received" )) == NULL ) {
		afc_dprintf(">>> No method registered for event drag_data_set\n");
		return;
	}

	/* Important, check if we actually got data.  Sometimes errors
	 * occure and selection_data will be NULL.
	 */
	if ( ( selection_data == NULL ) || ( selection_data->length < 0 ) ) {
		afc_dprintf(">>> No data received on DND event\n");
		return;
	}

        /* Source and target widgets are the same? */
	source_widget = gtk_drag_get_source_widget ( drag_context );
        same = ( source_widget == widget ) ? TRUE : FALSE;

	/* Now check if the data format type is one that we support
	 *
	 * We check this by testing if info matches one of the info
	 * values that we have defined.
	 *
	 * Note that we can also iterate through the atoms in:
	 *	GList *glist = drag_context->targets;
	 *
	 *	while(glist != NULL)
	 *	{
	 *	    gchar *name = gdk_atom_name((GdkAtom)glist->data);
	 *	     * strcmp the name to see if it matches
	 *	     * one that we support
	 *	     *
	 *	    glist = glist->next;
	 *	}
	 */

	afc_dprintf ( "%s: info: %d\n", __FUNCTION__, info );
	
	dnd_set(dyn, context, selection_data->data, selection_data->length, same);
	gtk_drag_finish(drag_context, TRUE, FALSE, 0);
}

/*
 *	DND "drag_motion" handler, this is called whenever the 
 *	pointer is dragging over the target widget.
 */
static gboolean afc_easygui_event_dnd_motion_received ( GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t, gpointer data )
{
	gboolean same_widget;
	GdkDragAction suggested_action;
	GtkWidget *src_widget, *tar_widget;
	DynamicClass * dyn = data;

        if( ( dyn == NULL) || ( dc == NULL ) ) return ( FALSE );

	afc_dprintf ( "%s: DND motion received: action %d\n", __FUNCTION__, dc->actions );

	/* Get source widget and target widget. */
	src_widget = gtk_drag_get_source_widget ( dc );
	tar_widget = widget;

	/* Note if source widget is the same as the target. */
	same_widget = ( src_widget == tar_widget ) ? TRUE : FALSE;


	/* If this is the same widget, our suggested action should be
	 * move.  For all other case we assume copy.
	 */
	if ( same_widget )
		suggested_action = GDK_ACTION_MOVE;
	else
		suggested_action = GDK_ACTION_COPY;

	/* Respond with default drag action (status). First we check
	 * the dc's list of actions. If the list only contains
	 * move, copy, or link then we select just that, otherwise we
	 * return with our default suggested action.
	 * If no valid actions are listed then we respond with 0.
	 */

        /* Only move? */
        if ( dc->actions == GDK_ACTION_MOVE ) gdk_drag_status ( dc, GDK_ACTION_MOVE, t );
        /* Only copy? */
        else if ( dc->actions == GDK_ACTION_COPY ) gdk_drag_status ( dc, GDK_ACTION_COPY, t );
        /* Only link? */
        else if ( dc->actions == GDK_ACTION_LINK ) gdk_drag_status ( dc, GDK_ACTION_LINK, t );
        /* Other action, check if listed in our actions list? */
        else if ( dc->actions & suggested_action ) gdk_drag_status ( dc, suggested_action, t );
        /* All else respond with 0. */
        else gdk_drag_status ( dc, 0, t );

	return ( FALSE );
}

/*
 *	DND "drag_begin" handler, this is called whenever a drag starts.
 */
static void afc_easygui_event_dnd_drag_begin ( GtkWidget * widget, GdkDragContext * dc, gpointer data )
{
	DynamicClass * dyn = data;

	afc_dprintf ( "%s\n", __FUNCTION__ );

        if ( ( widget == NULL ) || ( dyn == NULL ) || ( dc == NULL ) ) return;

	/* Put any needed drag begin setup code here. */
}

/*
 *      DND "drag_end" handler, this is called when a drag and drop has
 *	completed. So this function is the last one to be called in
 *	any given DND operation.
 */
static void afc_easygui_event_dnd_drag_end ( GtkWidget * widget, GdkDragContext * dc, gpointer data )
{
	DynamicClass * dyn = data;

	afc_dprintf ( "%s\n", __FUNCTION__ );

        if ( ( dyn == NULL ) || ( dc == NULL ) ) return;

	/* Put any needed drag end cleanup code here. */
}

/*
 *	DND "drag_data_delete" handler, this function is called when
 *	the data on the source `should' be deleted (ie if the DND was
 *	a move).
 */
static void afc_easygui_event_dnd_drag_data_delete ( GtkWidget * widget, GdkDragContext * dc, gpointer data )
{
        DynamicClass * dyn = data;

	afc_dprintf ( "%s\n", __FUNCTION__ );

        if ( ( widget == NULL ) || ( dyn == NULL) || ( dc == NULL ) ) return;

	/* Put any needed drag end cleanup code here. */
}  





/* =============================================================================================

*/
//DRAG_ACTIONs : (GDK_ACTION_DEFAULT|GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK|GDK_ACTION_PRIVATE|GDK_ACTION_ASK)

#define TEXT_PROTOCOL { "text/plain", 0, 1 }
#define DND_ACTIONS (GDK_ACTION_MOVE | GDK_ACTION_COPY)
#define DND_DEST_ACTIONS (GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP)

static GtkTargetEntry target_entry [] = { TEXT_PROTOCOL };

static void afc_easygui_widget_int_set_dnd_dest_connection ( EasyGUIContext * context, GtkWidget * w, DynamicClass * dyn )
{
	afc_dprintf ( "%s: setting widget to be a DND dest\n", __FUNCTION__ );
	gtk_drag_dest_set ( w, DND_DEST_ACTIONS , target_entry, 1, DND_DEST_ACTIONS );
		//gtk_signal_connect(GTK_OBJECT(w), "drag_data_received", G_CALLBACK(afc_easygui_event_dnd_received), NULL);
	
	//g_signal_connect ( G_OBJECT ( w ), "drag_motion", G_CALLBACK ( afc_easygui_event_dnd_motion_received ), dyn );
}

static void afc_easygui_widget_int_set_dnd_source_connection ( EasyGUIContext * context, GtkWidget * w, DynamicClass * dyn )
{
	// Set the drag source for this widget, allowing the user to drag items off of this widget.
	afc_dprintf ( "%s: setting widget to be a DND source\n", __FUNCTION__ );
	gtk_drag_source_set ( w, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, target_entry, 1, DND_DEST_ACTIONS );
		//gtk_signal_connect(GTK_OBJECT(w), "drag_data_get", G_CALLBACK(afc_easygui_event_dnd_get), NULL);

	/* Set DND signals on clist. */
	//g_signal_connect ( G_OBJECT ( w ), "drag_begin",    	G_CALLBACK ( afc_easygui_event_dnd_drag_begin ), dyn );
	//g_signal_connect ( G_OBJECT ( w ), "drag_end",      	G_CALLBACK ( afc_easygui_event_dnd_drag_end ),   dyn );
	//g_signal_connect ( G_OBJECT ( w ), "drag_data_delete", 	G_CALLBACK ( afc_easygui_event_dnd_drag_data_delete ), dyn );
}
